{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pandas 字符串长度和计数\n",
    "\n",
    "本教程介绍Pandas中字符串长度和计数的相关知识点和代码案例，包括`str.len()`和`str.count()`方法的使用。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 学习目标\n",
    "\n",
    "- 掌握`str.len()`方法获取字符串长度\n",
    "- 掌握`str.count()`方法统计子串出现次数\n",
    "- 了解在DataFrame中的应用\n",
    "- 掌握NaN值的处理方法\n",
    "- 学习实际应用案例\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 导入库\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. str.len() - 获取字符串长度\n",
    "\n",
    "`str.len()`方法用于获取Series中每个字符串的长度，返回一个数值Series。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 基本用法\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始数据:\n",
      "0        Python\n",
      "1        Pandas\n",
      "2         NumPy\n",
      "3    Matplotlib\n",
      "4       Seaborn\n",
      "dtype: object\n",
      "\n",
      "字符串长度:\n",
      "0     6\n",
      "1     6\n",
      "2     5\n",
      "3    10\n",
      "4     7\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "# 创建包含字符串的Series\n",
    "data = pd.Series(['Python', 'Pandas', 'NumPy', 'Matplotlib', 'Seaborn'])\n",
    "print(\"原始数据:\")\n",
    "print(data)\n",
    "\n",
    "# 获取每个字符串的长度\n",
    "print(\"\\n字符串长度:\")\n",
    "print(data.str.len())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 在DataFrame中的应用\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始DataFrame:\n",
      "      name              email        description\n",
      "0    Alice  alice@example.com     Data Scientist\n",
      "1      Bob       bob@test.com  Software Engineer\n",
      "2  Charlie   charlie@demo.com        ML Engineer\n",
      "3    David   david@sample.com       Data Analyst\n",
      "4      Eve       eve@mail.com         Researcher\n",
      "\n",
      "添加长度列后:\n",
      "      name              email        description  name_length  email_length  \\\n",
      "0    Alice  alice@example.com     Data Scientist            5            17   \n",
      "1      Bob       bob@test.com  Software Engineer            3            12   \n",
      "2  Charlie   charlie@demo.com        ML Engineer            7            16   \n",
      "3    David   david@sample.com       Data Analyst            5            16   \n",
      "4      Eve       eve@mail.com         Researcher            3            12   \n",
      "\n",
      "   description_length  \n",
      "0                  14  \n",
      "1                  17  \n",
      "2                  11  \n",
      "3                  12  \n",
      "4                  10  \n"
     ]
    }
   ],
   "source": [
    "# 创建DataFrame\n",
    "df = pd.DataFrame({\n",
    "    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],\n",
    "    'email': ['alice@example.com', 'bob@test.com', 'charlie@demo.com', 'david@sample.com', 'eve@mail.com'],\n",
    "    'description': ['Data Scientist', 'Software Engineer', 'ML Engineer', 'Data Analyst', 'Researcher']\n",
    "})\n",
    "\n",
    "print(\"原始DataFrame:\")\n",
    "print(df)\n",
    "\n",
    "# 计算各列字符串长度\n",
    "df['name_length'] = df['name'].str.len()\n",
    "df['email_length'] = df['email'].str.len()\n",
    "df['description_length'] = df['description'].str.len()\n",
    "\n",
    "print(\"\\n添加长度列后:\")\n",
    "print(df)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 处理NaN值\n",
    "\n",
    "`str.len()`方法会自动处理NaN值，返回NaN而不是抛出错误。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "包含NaN的数据:\n",
      "0     Python\n",
      "1     Pandas\n",
      "2        NaN\n",
      "3      NumPy\n",
      "4       None\n",
      "5    Seaborn\n",
      "dtype: object\n",
      "\n",
      "字符串长度（NaN返回NaN）:\n",
      "0    6.0\n",
      "1    6.0\n",
      "2    NaN\n",
      "3    5.0\n",
      "4    NaN\n",
      "5    7.0\n",
      "dtype: float64\n",
      "\n",
      "过滤NaN后计算长度:\n",
      "0    6\n",
      "1    6\n",
      "3    5\n",
      "5    7\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "# 创建包含NaN的Series\n",
    "data_with_nan = pd.Series(['Python', 'Pandas', np.nan, 'NumPy', None, 'Seaborn'])\n",
    "print(\"包含NaN的数据:\")\n",
    "print(data_with_nan)\n",
    "\n",
    "# 获取长度，NaN值返回NaN\n",
    "print(\"\\n字符串长度（NaN返回NaN）:\")\n",
    "lengths = data_with_nan.str.len()\n",
    "print(lengths)\n",
    "\n",
    "# 过滤掉NaN值后计算长度\n",
    "print(\"\\n过滤NaN后计算长度:\")\n",
    "print(data_with_nan.dropna().str.len())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 实际应用：数据验证\n",
    "\n",
    "使用`str.len()`可以验证数据长度是否符合要求，例如验证用户名长度、密码长度等。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "用户数据:\n",
      "     username              password\n",
      "0       alice               pass123\n",
      "1         bob                   pwd\n",
      "2  charlie123  secure_password_2024\n",
      "3           d                   123\n",
      "4   eve_smith            mypassword\n",
      "\n",
      "验证结果:\n",
      "     username  username_length  username_valid              password  \\\n",
      "0       alice                5            True               pass123   \n",
      "1         bob                3            True                   pwd   \n",
      "2  charlie123               10            True  secure_password_2024   \n",
      "3           d                1           False                   123   \n",
      "4   eve_smith                9            True            mypassword   \n",
      "\n",
      "   password_length  password_valid  \n",
      "0                7           False  \n",
      "1                3           False  \n",
      "2               20            True  \n",
      "3                3           False  \n",
      "4               10            True  \n",
      "\n",
      "不符合要求的数据:\n",
      "  username password\n",
      "0    alice  pass123\n",
      "1      bob      pwd\n",
      "3        d      123\n"
     ]
    }
   ],
   "source": [
    "# 用户注册数据验证示例\n",
    "users = pd.DataFrame({\n",
    "    'username': ['alice', 'bob', 'charlie123', 'd', 'eve_smith'],\n",
    "    'password': ['pass123', 'pwd', 'secure_password_2024', '123', 'mypassword']\n",
    "})\n",
    "\n",
    "print(\"用户数据:\")\n",
    "print(users)\n",
    "\n",
    "# 验证用户名长度（要求3-20个字符）\n",
    "users['username_length'] = users['username'].str.len()\n",
    "users['username_valid'] = (users['username_length'] >= 3) & (users['username_length'] <= 20)\n",
    "\n",
    "# 验证密码长度（要求至少8个字符）\n",
    "users['password_length'] = users['password'].str.len()\n",
    "users['password_valid'] = users['password_length'] >= 8\n",
    "\n",
    "print(\"\\n验证结果:\")\n",
    "print(users[['username', 'username_length', 'username_valid', \n",
    "             'password', 'password_length', 'password_valid']])\n",
    "\n",
    "# 找出不符合要求的数据\n",
    "invalid_users = users[~(users['username_valid'] & users['password_valid'])]\n",
    "print(\"\\n不符合要求的数据:\")\n",
    "print(invalid_users[['username', 'password']])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. str.count() - 统计子串出现次数\n",
    "\n",
    "`str.count()`方法用于统计Series中每个字符串内指定子串出现的次数。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 基本用法\n",
    "\n",
    "语法：`str.count(sub, start=0, end=None)`\n",
    "- `sub`: 要统计的子串\n",
    "- `start`: 开始位置（可选）\n",
    "- `end`: 结束位置（可选）\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始数据:\n",
      "0                hello\n",
      "1          hello world\n",
      "2    hello hello hello\n",
      "3                world\n",
      "4               python\n",
      "dtype: object\n",
      "\n",
      "统计'hello'出现的次数:\n",
      "0    1\n",
      "1    1\n",
      "2    3\n",
      "3    0\n",
      "4    0\n",
      "dtype: int64\n",
      "\n",
      "统计字母'o'出现的次数:\n",
      "0    1\n",
      "1    2\n",
      "2    3\n",
      "3    1\n",
      "4    1\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "# 创建包含重复字符的字符串\n",
    "data = pd.Series(['hello', 'hello world', 'hello hello hello', 'world', 'python'])\n",
    "print(\"原始数据:\")\n",
    "print(data)\n",
    "\n",
    "# 统计'hello'出现的次数\n",
    "print(\"\\n统计'hello'出现的次数:\")\n",
    "print(data.str.count('hello'))\n",
    "\n",
    "# 统计字母'o'出现的次数\n",
    "print(\"\\n统计字母'o'出现的次数:\")\n",
    "print(data.str.count('o'))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2 指定搜索范围\n",
    "\n",
    "可以使用`start`和`end`参数指定搜索范围。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始数据:\n",
      "0     hello world hello\n",
      "1    python programming\n",
      "2     data science data\n",
      "dtype: object\n",
      "\n",
      "在整个字符串中统计'hello':\n",
      "0    2\n",
      "1    0\n",
      "2    0\n",
      "dtype: int64\n",
      "\n",
      "在前10个字符中统计'hello':\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "StringMethods.count() got an unexpected keyword argument 'start'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 12\u001b[39m\n\u001b[32m     10\u001b[39m \u001b[38;5;66;03m# 在前10个字符中统计'hello'\u001b[39;00m\n\u001b[32m     11\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m在前10个字符中统计\u001b[39m\u001b[33m'\u001b[39m\u001b[33mhello\u001b[39m\u001b[33m'\u001b[39m\u001b[33m:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m12\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mtext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstr\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcount\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mhello\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstart\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mend\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m10\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[32m     14\u001b[39m \u001b[38;5;66;03m# 从第5个字符开始统计'hello'\u001b[39;00m\n\u001b[32m     15\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m从第5个字符开始统计\u001b[39m\u001b[33m'\u001b[39m\u001b[33mhello\u001b[39m\u001b[33m'\u001b[39m\u001b[33m:\u001b[39m\u001b[33m\"\u001b[39m)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m/opt/anaconda3/envs/ml311/lib/python3.11/site-packages/pandas/core/strings/accessor.py:140\u001b[39m, in \u001b[36mforbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m    135\u001b[39m     msg = (\n\u001b[32m    136\u001b[39m         \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mCannot use .str.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with values of \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    137\u001b[39m         \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33minferred dtype \u001b[39m\u001b[33m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m._inferred_dtype\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    138\u001b[39m     )\n\u001b[32m    139\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[32m--> \u001b[39m\u001b[32m140\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mTypeError\u001b[39m: StringMethods.count() got an unexpected keyword argument 'start'"
     ]
    }
   ],
   "source": [
    "# 创建示例数据\n",
    "text = pd.Series(['hello world hello', 'python programming', 'data science data'])\n",
    "print(\"原始数据:\")\n",
    "print(text)\n",
    "\n",
    "# 在整个字符串中统计'hello'\n",
    "print(\"\\n在整个字符串中统计'hello':\")\n",
    "print(text.str.count('hello'))\n",
    "\n",
    "# 在前10个字符中统计'hello'\n",
    "print(\"\\n在前10个字符中统计'hello':\")\n",
    "print(text.str.count('hello', start=0, end=10))\n",
    "\n",
    "# 从第5个字符开始统计'hello'\n",
    "print(\"\\n从第5个字符开始统计'hello':\")\n",
    "print(text.str.count('hello', start=5))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3 处理NaN值\n",
    "\n",
    "`str.count()`方法同样会自动处理NaN值。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 包含NaN的数据\n",
    "data_with_nan = pd.Series(['hello world', 'hello', np.nan, 'world hello', None])\n",
    "print(\"包含NaN的数据:\")\n",
    "print(data_with_nan)\n",
    "\n",
    "# 统计'hello'出现次数，NaN返回NaN\n",
    "print(\"\\n统计'hello'出现次数:\")\n",
    "print(data_with_nan.str.count('hello'))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4 实际应用：统计特定字符或模式\n",
    "\n",
    "使用`str.count()`可以统计特定字符、单词或模式的出现次数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 示例1：统计邮件地址中'@'符号的数量（应该都是1）\n",
    "emails = pd.Series([\n",
    "    'alice@example.com',\n",
    "    'bob@test.com',\n",
    "    'charlie@demo.com',\n",
    "    'invalid_email',  # 没有@符号\n",
    "    'double@at@test.com'  # 两个@符号\n",
    "])\n",
    "\n",
    "print(\"邮件地址:\")\n",
    "print(emails)\n",
    "\n",
    "# 统计@符号数量\n",
    "at_count = emails.str.count('@')\n",
    "print(\"\\n@符号数量:\")\n",
    "print(at_count)\n",
    "\n",
    "# 找出无效的邮件地址（@符号数量不等于1）\n",
    "invalid_emails = emails[at_count != 1]\n",
    "print(\"\\n无效的邮件地址:\")\n",
    "print(invalid_emails)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 示例2：统计文本中空格的数量\n",
    "texts = pd.Series([\n",
    "    'Hello World',\n",
    "    'Python Programming',\n",
    "    'Data Science',\n",
    "    'MachineLearning',  # 没有空格\n",
    "    'Natural Language Processing'\n",
    "])\n",
    "\n",
    "print(\"文本数据:\")\n",
    "print(texts)\n",
    "\n",
    "# 统计空格数量\n",
    "space_count = texts.str.count(' ')\n",
    "print(\"\\n空格数量:\")\n",
    "print(space_count)\n",
    "\n",
    "# 根据空格数量判断单词数（空格数+1）\n",
    "word_count = space_count + 1\n",
    "print(\"\\n估算的单词数（空格数+1）:\")\n",
    "print(word_count)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 示例3：统计代码中特定关键词的出现次数\n",
    "code_snippets = pd.DataFrame({\n",
    "    'code': [\n",
    "        'def function(): return True',\n",
    "        'class MyClass: pass',\n",
    "        'import pandas as pd',\n",
    "        'def calculate(x, y): return x + y',\n",
    "        'from numpy import array'\n",
    "    ]\n",
    "})\n",
    "\n",
    "print(\"代码片段:\")\n",
    "print(code_snippets)\n",
    "\n",
    "# 统计'def'关键词出现次数\n",
    "code_snippets['def_count'] = code_snippets['code'].str.count('def')\n",
    "# 统计'import'关键词出现次数\n",
    "code_snippets['import_count'] = code_snippets['code'].str.count('import')\n",
    "# 统计'return'关键词出现次数\n",
    "code_snippets['return_count'] = code_snippets['code'].str.count('return')\n",
    "\n",
    "print(\"\\n关键词统计:\")\n",
    "print(code_snippets)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 综合应用案例\n",
    "\n",
    "### 4.1 文本分析：统计字符和单词\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建文本数据\n",
    "articles = pd.DataFrame({\n",
    "    'title': [\n",
    "        'Introduction to Python',\n",
    "        'Data Science with Pandas',\n",
    "        'Machine Learning Basics',\n",
    "        'Deep Learning Tutorial',\n",
    "        'Natural Language Processing'\n",
    "    ],\n",
    "    'content': [\n",
    "        'Python is a powerful programming language.',\n",
    "        'Pandas makes data analysis easy and efficient.',\n",
    "        'Machine learning is transforming industries.',\n",
    "        'Deep learning uses neural networks.',\n",
    "        'NLP helps computers understand human language.'\n",
    "    ]\n",
    "})\n",
    "\n",
    "print(\"文章数据:\")\n",
    "print(articles)\n",
    "\n",
    "# 分析文本特征\n",
    "articles['title_length'] = articles['title'].str.len()\n",
    "articles['content_length'] = articles['content'].str.len()\n",
    "articles['title_words'] = articles['title'].str.count(' ') + 1\n",
    "articles['content_words'] = articles['content'].str.count(' ') + 1\n",
    "articles['title_vowels'] = (articles['title'].str.count('a') + \n",
    "                           articles['title'].str.count('e') + \n",
    "                           articles['title'].str.count('i') + \n",
    "                           articles['title'].str.count('o') + \n",
    "                           articles['title'].str.count('u'))\n",
    "\n",
    "print(\"\\n文本分析结果:\")\n",
    "print(articles[['title', 'title_length', 'title_words', 'title_vowels', \n",
    "                'content_length', 'content_words']])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 数据质量检查：验证格式\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 电话号码格式验证\n",
    "phone_numbers = pd.DataFrame({\n",
    "    'phone': [\n",
    "        '138-1234-5678',\n",
    "        '139-1234-5678',\n",
    "        '13812345678',  # 缺少分隔符\n",
    "        '138-123-45678',  # 格式错误\n",
    "        '138-1234-567',  # 长度不足\n",
    "        '138-1234-56789'  # 长度过长\n",
    "    ]\n",
    "})\n",
    "\n",
    "print(\"电话号码数据:\")\n",
    "print(phone_numbers)\n",
    "\n",
    "# 验证格式\n",
    "phone_numbers['length'] = phone_numbers['phone'].str.len()\n",
    "phone_numbers['dash_count'] = phone_numbers['phone'].str.count('-')\n",
    "\n",
    "# 统计数字数量（str.count()不支持正则表达式，需要逐个统计）\n",
    "def count_digits(text):\n",
    "    if pd.isna(text):\n",
    "        return np.nan\n",
    "    return sum(text.count(str(i)) for i in range(10))\n",
    "\n",
    "phone_numbers['digit_count'] = phone_numbers['phone'].apply(count_digits)\n",
    "\n",
    "# 基本验证：长度和分隔符数量\n",
    "phone_numbers['is_valid_format'] = (\n",
    "    (phone_numbers['length'] == 13) &  # 总长度13（包括2个分隔符）\n",
    "    (phone_numbers['dash_count'] == 2)  # 2个分隔符\n",
    ")\n",
    "\n",
    "print(\"\\n验证结果:\")\n",
    "print(phone_numbers)\n",
    "\n",
    "# 找出无效格式\n",
    "invalid_phones = phone_numbers[~phone_numbers['is_valid_format']]\n",
    "print(\"\\n无效格式的电话号码:\")\n",
    "print(invalid_phones[['phone']])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3 密码强度检查\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 密码强度检查\n",
    "passwords = pd.DataFrame({\n",
    "    'password': [\n",
    "        'Password123!',\n",
    "        'weak',\n",
    "        'StrongP@ssw0rd',\n",
    "        '12345678',\n",
    "        'MyP@ss123',\n",
    "        'VeryStrongP@ssw0rd2024!'\n",
    "    ]\n",
    "})\n",
    "\n",
    "print(\"密码数据:\")\n",
    "print(passwords)\n",
    "\n",
    "# 密码强度分析\n",
    "passwords['length'] = passwords['password'].str.len()\n",
    "\n",
    "# 定义辅助函数统计字符类型（str.count()不支持正则表达式）\n",
    "def count_uppercase(text):\n",
    "    if pd.isna(text):\n",
    "        return np.nan\n",
    "    return sum(1 for c in text if c.isupper())\n",
    "\n",
    "def count_lowercase(text):\n",
    "    if pd.isna(text):\n",
    "        return np.nan\n",
    "    return sum(1 for c in text if c.islower())\n",
    "\n",
    "def count_digits(text):\n",
    "    if pd.isna(text):\n",
    "        return np.nan\n",
    "    return sum(text.count(str(i)) for i in range(10))\n",
    "\n",
    "def count_special(text):\n",
    "    if pd.isna(text):\n",
    "        return np.nan\n",
    "    special_chars = '!@#$%^&*(),.?\":{}|<>'\n",
    "    return sum(text.count(c) for c in special_chars)\n",
    "\n",
    "passwords['uppercase_count'] = passwords['password'].apply(count_uppercase)\n",
    "passwords['lowercase_count'] = passwords['password'].apply(count_lowercase)\n",
    "passwords['digit_count'] = passwords['password'].apply(count_digits)\n",
    "passwords['special_count'] = passwords['password'].apply(count_special)\n",
    "\n",
    "# 计算密码强度分数\n",
    "passwords['strength_score'] = (\n",
    "    (passwords['length'] >= 8).astype(int) * 2 +  # 长度要求\n",
    "    (passwords['uppercase_count'] > 0).astype(int) +  # 有大写字母\n",
    "    (passwords['lowercase_count'] > 0).astype(int) +  # 有小写字母\n",
    "    (passwords['digit_count'] > 0).astype(int) +  # 有数字\n",
    "    (passwords['special_count'] > 0).astype(int)  # 有特殊字符\n",
    ")\n",
    "\n",
    "# 根据分数判断强度\n",
    "def get_strength(score):\n",
    "    if score >= 6:\n",
    "        return '强'\n",
    "    elif score >= 4:\n",
    "        return '中'\n",
    "    else:\n",
    "        return '弱'\n",
    "\n",
    "passwords['strength'] = passwords['strength_score'].apply(get_strength)\n",
    "\n",
    "print(\"\\n密码强度分析:\")\n",
    "print(passwords[['password', 'length', 'uppercase_count', 'lowercase_count', \n",
    "                 'digit_count', 'special_count', 'strength_score', 'strength']])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 统计数字字符数量（不使用正则表达式）\n",
    "texts = pd.Series(['abc123', 'test456', '789xyz', 'no_numbers'])\n",
    "\n",
    "print(\"文本数据:\")\n",
    "print(texts)\n",
    "\n",
    "# 方法1：逐个统计每个数字\n",
    "def count_digits(text):\n",
    "    if pd.isna(text):\n",
    "        return np.nan\n",
    "    return sum(text.count(str(i)) for i in range(10))\n",
    "\n",
    "texts['digit_count'] = texts.apply(count_digits)\n",
    "print(\"\\n数字字符数量:\")\n",
    "print(texts)\n",
    "\n",
    "# 方法2：使用列表推导式（更简洁）\n",
    "texts['digit_count_v2'] = texts.apply(\n",
    "    lambda x: sum(x.count(str(i)) for i in range(10)) if pd.notna(x) else np.nan\n",
    ")\n",
    "print(\"\\n方法2结果:\")\n",
    "print(texts[['digit_count', 'digit_count_v2']])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 注意事项和最佳实践\n",
    "\n",
    "### 5.1 str.count()不支持正则表达式\n",
    "\n",
    "`str.count()`方法不支持正则表达式，它只进行字面匹配。如果需要正则表达式，可以使用`str.contains()`配合其他方法。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# str.count()只进行字面匹配\n",
    "data = pd.Series(['hello', 'hello world', 'Hello', 'HELLO'])\n",
    "\n",
    "print(\"原始数据:\")\n",
    "print(data)\n",
    "\n",
    "# 统计'hello'（区分大小写）\n",
    "print(\"\\n统计'hello'（区分大小写）:\")\n",
    "print(data.str.count('hello'))\n",
    "\n",
    "# 注意：str.count()不会匹配'Hello'或'HELLO'\n",
    "# 如果需要不区分大小写，需要先转换为小写\n",
    "print(\"\\n转换为小写后统计'hello':\")\n",
    "print(data.str.lower().str.count('hello'))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2 性能考虑\n",
    "\n",
    "对于大量数据，`str.len()`和`str.count()`都是向量化操作，性能很好。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 性能测试\n",
    "import time\n",
    "\n",
    "# 创建大量数据\n",
    "large_data = pd.Series(['Python Programming'] * 100000)\n",
    "\n",
    "# 测试str.len()性能\n",
    "start = time.time()\n",
    "lengths = large_data.str.len()\n",
    "time_len = time.time() - start\n",
    "print(f\"str.len()处理100,000条数据耗时: {time_len:.4f}秒\")\n",
    "\n",
    "# 测试str.count()性能\n",
    "start = time.time()\n",
    "counts = large_data.str.count('P')\n",
    "time_count = time.time() - start\n",
    "print(f\"str.count()处理100,000条数据耗时: {time_count:.4f}秒\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.3 链式操作\n",
    "\n",
    "`str.len()`和`str.count()`可以与其他字符串方法链式使用。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 链式操作示例\n",
    "data = pd.Series(['  Python  ', '  PANDAS  ', '  numpy  '])\n",
    "\n",
    "print(\"原始数据:\")\n",
    "print(data)\n",
    "\n",
    "# 链式操作：去除空白 -> 转小写 -> 获取长度\n",
    "cleaned_length = data.str.strip().str.lower().str.len()\n",
    "print(\"\\n链式操作结果（去除空白 -> 转小写 -> 获取长度）:\")\n",
    "print(cleaned_length)\n",
    "\n",
    "# 链式操作：去除空白 -> 统计特定字符\n",
    "space_count = data.str.strip().str.count('a')\n",
    "print(\"\\n去除空白后统计'a'的数量:\")\n",
    "print(space_count)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 总结\n",
    "\n",
    "### 6.1 关键要点\n",
    "\n",
    "1. **str.len()**: 获取字符串长度，返回数值Series\n",
    "   - 自动处理NaN值\n",
    "   - 适用于数据验证和文本分析\n",
    "\n",
    "2. **str.count()**: 统计子串出现次数\n",
    "   - 只进行字面匹配，不支持正则表达式\n",
    "   - 可以指定搜索范围（start和end参数）\n",
    "   - 自动处理NaN值\n",
    "\n",
    "3. **应用场景**:\n",
    "   - 数据验证（长度检查、格式验证）\n",
    "   - 文本分析（字符统计、单词计数）\n",
    "   - 密码强度检查\n",
    "   - 数据质量检查\n",
    "\n",
    "### 6.2 最佳实践\n",
    "\n",
    "- 使用`str.len()`进行数据长度验证\n",
    "- 使用`str.count()`统计特定字符或子串\n",
    "- 注意`str.count()`区分大小写，需要时先转换大小写\n",
    "- 利用链式操作简化代码\n",
    "- 对于大量数据，这些方法都是向量化的，性能很好\n",
    "\n",
    "### 6.3 下一步学习\n",
    "\n",
    "掌握了字符串长度和计数后，可以继续学习：\n",
    "- 字符串查找和匹配（str.contains, str.find等）\n",
    "- 字符串替换（str.replace）\n",
    "- 字符串分割和连接\n",
    "- 正则表达式操作\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ml311",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
